#include"vcryd.h"

extern "C" VOID vcrydThread(PVOID Context)
{
	DbgPrint("in vcrydThread\n");
			PDEVICE_OBJECT      pDeviceObject;
			PDEVICE_EXTENSION   pDeviceExtension;
			PLIST_ENTRY         request;
			PIRP                Irp;
			PIO_STACK_LOCATION  pIrpStack;
			PUCHAR              system_buffer;
			PUCHAR              buffer;
			HANDLE				hFile;
			NTSTATUS			status;
			ULONG				byteSent;
			//ASSERT(Context != NULL);

			pDeviceObject = (PDEVICE_OBJECT)Context;
			
			pDeviceExtension = (PDEVICE_EXTENSION)pDeviceObject->DeviceExtension;
			KeSetPriorityThread(KeGetCurrentThread(), LOW_REALTIME_PRIORITY);
			while(1)
			{
				KeWaitForSingleObject(&pDeviceExtension->request_event,
										Executive,
										KernelMode,
										FALSE,
										NULL
										);

										if (pDeviceExtension->terminate_thread)
										{
											PsTerminateSystemThread(STATUS_SUCCESS);
										}
				while (request = ExInterlockedRemoveHeadList(
					&pDeviceExtension->list_head,
					&pDeviceExtension->list_lock
					))
				{
					Irp = CONTAINING_RECORD(request, IRP, Tail.Overlay.ListEntry);

					pIrpStack = IoGetCurrentIrpStackLocation(Irp);
				
				
										switch (pIrpStack->MajorFunction)
										{
											case IRP_MJ_READ:
											{
												DbgPrint("in vcrydThread IRP_MJ_READ\n");
												DbgPrint("\t\tRead.Length=%d\tRead.ByteOffset=%x \n",pIrpStack->Parameters.Read.Length,&pIrpStack->Parameters.Read.ByteOffset.QuadPart);
																	
												hFile=pDeviceExtension->file_handle;
							
												PVOID systemBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
																			if (systemBuffer == NULL)
																			{
																				DbgPrint("SystemBuffer==NULL\n");
																				Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
																				Irp->IoStatus.Information = 0;
																				status = CompleteIrp(Irp, status, Irp->IoStatus.Information );
																		
																			}
												PVOID buffer = ExAllocatePoolWithTag(PagedPool, pIrpStack->Parameters.Read.Length,0);
												
																			if (buffer == NULL)
																			{
																				DbgPrint("user buffer==NULL\n");
																				Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
																				Irp->IoStatus.Information = 0;
																				byteSent=0;
																				status = CompleteIrp(Irp, status, byteSent);
																			}
													
												
																	DbgPrint("Buffers are ok\n");											
																	LARGE_INTEGER ByteOffset;
																	ByteOffset.QuadPart=pIrpStack->Parameters.Read.ByteOffset.QuadPart+HEADER_OFFSET;
																	
													status=ZwReadFile(
													hFile,
													NULL,
													NULL,
													NULL,
													&Irp->IoStatus,
													buffer,
													pIrpStack->Parameters.Read.Length,
													&ByteOffset,//&pIrpStack->Parameters.Read.ByteOffset,
													NULL
													);
																			if (status!=STATUS_SUCCESS||Irp->IoStatus.Status != STATUS_SUCCESS)
																			{
																				DbgPrint("ZwReadFile status=%x\thandle=%x\tInfo=%u\n",Irp->IoStatus.Status,hFile,Irp->IoStatus.Information);
																			}
												status= DeCrypt(buffer, pDeviceExtension->key, pIrpStack->Parameters.Read.Length);
																			if (status != STATUS_SUCCESS)
																			{
																				DbgPrint("DeCript didn't sent STATUS_SUCCESS \n");
																				status = STATUS_INTERNAL_ERROR;
																			}
												
												
												RtlCopyMemory(systemBuffer, buffer, pIrpStack->Parameters.Read.Length);
												ExFreePool(buffer);
												
												CompleteIrp(Irp,status,Irp->IoStatus.Information);
												
												break;
											}//case IRP_MJ_READ
											case IRP_MJ_WRITE:
											{
												
												DbgPrint("in vcrydThread IRP_MJ_Write\n");
												DbgPrint("\t\tWrite.Length=%d\tWrite.ByteOffset=%x \n",pIrpStack->Parameters.Write.Length,&pIrpStack->Parameters.Write.ByteOffset.QuadPart);
												if ((pIrpStack->Parameters.Write.ByteOffset.QuadPart +
																	pIrpStack->Parameters.Write.Length) >
																	pDeviceExtension->file_size.QuadPart)
																{
																	DbgPrint("in thread Trying to write futher then end\n");												
																	Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
																	Irp->IoStatus.Information = 0;
																	break;
																}
																hFile=pDeviceExtension->file_handle;
							
												PVOID systemBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
																			if (systemBuffer == NULL)
																			{
																				DbgPrint("SystemBuffer==NULL\n");
																				Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
																				Irp->IoStatus.Information = 0;
																				status = CompleteIrp(Irp, status, Irp->IoStatus.Information );
																				break;
																			}
												PVOID buffer = ExAllocatePoolWithTag(PagedPool, pIrpStack->Parameters.Write.Length,0);
																			if (buffer == NULL)
																			{
																				DbgPrint("user buffer==NULL\n");
																				Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
																				Irp->IoStatus.Information = 0;
																				byteSent=0;
																				status = CompleteIrp(Irp, status, byteSent);
																				break;
																			}
												
												
																	DbgPrint("Buffers are ok\n");
												
													RtlCopyMemory(buffer,systemBuffer, pIrpStack->Parameters.Read.Length);				
													status= Crypt(buffer, pDeviceExtension->key, pIrpStack->Parameters.Read.Length);
													//status = Crypt(pDeviceObject, Irp);
																if (status != STATUS_SUCCESS)
																{
																	DbgPrint("Crypt didn't sent STATUS_SUCCESS \n");
																	status = STATUS_INTERNAL_ERROR;
																	break;
																}
																LARGE_INTEGER ByteOffset;
																ByteOffset.QuadPart=pIrpStack->Parameters.Read.ByteOffset.QuadPart+HEADER_OFFSET;
													status=ZwWriteFile(
													pDeviceExtension->file_handle,
													NULL,
													NULL,
													NULL,
													&Irp->IoStatus,
													buffer,
													pIrpStack->Parameters.Write.Length,
													&ByteOffset,//&pIrpStack->Parameters.Write.ByteOffset,
													NULL
													);
																if (status!=STATUS_SUCCESS||Irp->IoStatus.Status != STATUS_SUCCESS)
																{
																	DbgPrint("ZwWriteFile status=%x\thandle=%x\tInfo=%u\n",Irp->IoStatus.Status,hFile,Irp->IoStatus.Information);
																}
																
												
												ExFreePool(buffer);
												CompleteIrp(Irp,status,Irp->IoStatus.Information);
												break;


											}//case IRP_MJ_WRITE
										}//switch (pIrpStack->MajorFunction)
					}//while (request = ExInterlockedRemoveHeadList
			}//while(1)
}
extern "C" NTSTATUS VCRYDReadWriteDispatch(PDEVICE_OBJECT pDeviceObject, PIRP Irp)
{
	DbgPrint("vcrydReadWriteDispatch\n");
			PDEVICE_EXTENSION   pDeviceExtension;
			PIO_STACK_LOCATION  pIrpStack;

			pDeviceExtension = (PDEVICE_EXTENSION)pDeviceObject->DeviceExtension;

			if (!pDeviceExtension->media_in_device)
			{
				Irp->IoStatus.Status = STATUS_NO_MEDIA_IN_DEVICE;
				Irp->IoStatus.Information = 0;

				IoCompleteRequest(Irp, IO_NO_INCREMENT);

				return STATUS_NO_MEDIA_IN_DEVICE;
			}

			pIrpStack = IoGetCurrentIrpStackLocation(Irp);

									if (pIrpStack->Parameters.Read.Length == 0)
									{
										Irp->IoStatus.Status = STATUS_SUCCESS;
										Irp->IoStatus.Information = 0;

										IoCompleteRequest(Irp, IO_NO_INCREMENT);

										return STATUS_SUCCESS;
									}

			IoMarkIrpPending(Irp);

			ExInterlockedInsertTailList(
				&pDeviceExtension->list_head,
				&Irp->Tail.Overlay.ListEntry,
				&pDeviceExtension->list_lock
				);

			KeSetEvent(
				&pDeviceExtension->request_event,
				(KPRIORITY)0,
				FALSE
				);

			return STATUS_PENDING;
		
}
extern "C" NTSTATUS VCRYDcreateThread(PDEVICE_OBJECT pDeviceObject)
{
HANDLE threadHandle=0;
NTSTATUS status=STATUS_SUCCESS;
PDEVICE_EXTENSION pDeviceExtension=0;
pDeviceExtension=(PDEVICE_EXTENSION)pDeviceObject->DeviceExtension;

			InitializeListHead(&pDeviceExtension->list_head);

			KeInitializeSpinLock(&pDeviceExtension->list_lock);

			KeInitializeEvent(
				&pDeviceExtension->request_event,
				SynchronizationEvent,
				FALSE
				);

			pDeviceExtension->terminate_thread = FALSE;

			status = PsCreateSystemThread(
				&threadHandle,
				(ACCESS_MASK)0L,
				NULL,
				NULL,
				NULL,
				vcrydThread,
				pDeviceObject
				);

			if (!NT_SUCCESS(status))
			{
				DbgPrint("Thread not created in %s line(%d) \n",__FUNCTION__,__LINE__);
				
				IoDeleteDevice(pDeviceObject);
				return status;
			}

			status = ObReferenceObjectByHandle(
				threadHandle,
				THREAD_ALL_ACCESS,
				NULL,
				KernelMode,
				&pDeviceExtension->thread_pointer,
				NULL
				);

			if (!NT_SUCCESS(status))
			{
				//KdPrint("Thread not referented by handle in %s line(%d) \n",__FUNCTION__,__LINE__);
				DbgPrint("Thread not referented by handle in %s line(%d) \n",__FUNCTION__,__LINE__);
				ZwClose(threadHandle);

				pDeviceExtension->terminate_thread = TRUE;

				KeSetEvent(
					&pDeviceExtension->request_event,
					(KPRIORITY)0,
					FALSE
					);
				IoDeleteDevice(pDeviceObject);
			}
			return status;
}
